/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "NasArchiveRestore.h"
#include <vector>
#include "common/File.h"
#include "system/System.hpp"
#include "common/CleanMemPwd.h"
#include "common/EnvVarManager.h"
#include "constant/ErrorCodes.h"
#include "ClientInvoke.h"
#include "PluginUtilities.h"
#include "ScanMgr.h"
#include "ShareResourceManager.h"
#include "NfsContextWrapper.h"

#include "PluginUtilities.h"
using namespace PluginUtils;
using namespace std;

namespace {
const auto MODULE = "NasArchiveRestore";
constexpr auto BACKUP_KEY_SUFFIX = "_backup_stats";
constexpr int PREPARE_ARCHIVE_CLIENT_INTERVAL = 10;
constexpr uint32_t CTRL_FILE_CNT = 100;
constexpr int REPORT_MONITOR_TIME = 30;
const std::string SCAN_CTRL_MAX_DATA_SIZE = "10737418240";
const std::string SCAN_CTRL_MIN_DATA_SIZE = "5368709120";
constexpr uint64_t SCAN_META_FILE_SIZE = 1024 * 1024 * 1024;
constexpr uint32_t BACKUP_MAX_BUF_SIZE = 10 * 1024;
constexpr uint32_t BACKUP_BLOCK_SIZE = 4 * 1024 * 1024;
constexpr uint32_t BACKUP_BLOCK_SIZE_1_MB = 1024 * 1024;
constexpr uint32_t BACKUP_BLOCK_SIZE_128_KB = 128 * 1024;
constexpr uint32_t SCANNER_REPORT_CIRCLE_TIME = 60;
constexpr int INITIAL_ERROR_CODE = 0;
constexpr auto FILELEVELRESTORENATIVE = "native";
constexpr uint32_t NUMBER4000 = 4000;
constexpr uint32_t NUMBER1000 = 1000;
constexpr uint32_t NUMBER10000 = 10000;
constexpr uint32_t NUM_10 = 10;
const uint32_t FOUR_MB = 4 * 1024 * 1024;
// 是磁带恢复的话，每4M有18个字节的控制头，需要去掉
const uint32_t TAPE_READ_BUFFER_SIZE = FOUR_MB - 18;
const int TAP_REMIND_ERROR = 17;

struct RestoreJobExtendInfo {
    std::string failedScript;
    std::string postScript;
    std::string preScript;
    std::string restoreOption;

    BEGIN_SERIAL_MEMEBER
    SERIAL_MEMBER_TO_SPECIFIED_NAME(failedScript, failed_script)
    SERIAL_MEMBER_TO_SPECIFIED_NAME(postScript, post_script)
    SERIAL_MEMBER_TO_SPECIFIED_NAME(preScript, pre_script)
    SERIAL_MEMBER_TO_SPECIFIED_NAME(restoreOption, restoreOption)
    END_SERIAL_MEMEBER
};

struct FileSetInfo {
    std::string filters;
    std::string paths;
    std::string templateId;
    std::string templateName;

    BEGIN_SERIAL_MEMEBER
    SERIAL_MEMBER_TO_SPECIFIED_NAME(filters, filters)
    SERIAL_MEMBER_TO_SPECIFIED_NAME(paths, paths)
    SERIAL_MEMBER_TO_SPECIFIED_NAME(templateId, templateId)
    SERIAL_MEMBER_TO_SPECIFIED_NAME(templateName, templateName)
    END_SERIAL_MEMEBER
};

struct ExtendArchiveInfo {
    struct IpPort {
        std::string ip;
        int port { -1 };
        BEGIN_SERIAL_MEMEBER
        SERIAL_MEMBER_TO_SPECIFIED_NAME(ip, ip)
        SERIAL_MEMBER_TO_SPECIFIED_NAME(port, port)
        END_SERIAL_MEMEBER
    };

    std::vector<IpPort> serviceInfo;
    bool enableSSL {false};
    BEGIN_SERIAL_MEMEBER
    SERIAL_MEMBER_TO_SPECIFIED_NAME(enableSSL, enable_ssl)
    SERIAL_MEMBER_TO_SPECIFIED_NAME(serviceInfo, service_info)
    END_SERIAL_MEMEBER
};

// 从extendinfo中获取备份副本的原始信息
struct RepositoryInfo {
    struct AuthInfo {
        std::string authKey;
        std::string authPwd;
        int authType {-1};
        BEGIN_SERIAL_MEMEBER
        SERIAL_MEMBER_TO_SPECIFIED_NAME(authKey, authKey)
        SERIAL_MEMBER_TO_SPECIFIED_NAME(authPwd, authPwd)
        SERIAL_MEMBER_TO_SPECIFIED_NAME(authType, authType)
        END_SERIAL_MEMEBER
    };
    AuthInfo authInfo {};
    int type {-1};
    BEGIN_SERIAL_MEMEBER
    SERIAL_MEMBER_TO_SPECIFIED_NAME(authInfo, auth)
    SERIAL_MEMBER_TO_SPECIFIED_NAME(type, type)
    END_SERIAL_MEMEBER
};

// 备份副本仓信息
struct ArchiveCopyExtendInfo {
    std::vector<RepositoryInfo> repositoryInfo;
    BEGIN_SERIAL_MEMEBER
    SERIAL_MEMBER_TO_SPECIFIED_NAME(repositoryInfo, repositories)
    END_SERIAL_MEMEBER
};

} // namespace

NasArchiveRestore::~NasArchiveRestore()
{
    // m_cacheFs
    Module::CleanMemoryPwd(m_nasShare.password);
    Module::CleanMemoryPwd(m_nasShare.encryption);
    Module::CleanMemoryPwd(m_nasShare.auth.authPwd);
    Module::CleanMemoryPwd(m_nasShare.auth.extendInfo);
    Module::CleanMemoryPwd(m_fileSystemSourceInfo.authPwd);
    if (m_jobInfoPtr) {
        Module::CleanMemoryPwd(m_jobInfoPtr->targetObject.auth.authPwd);
        Module::CleanMemoryPwd(m_jobInfoPtr->extendInfo);
    }
}

bool NasArchiveRestore::GetRestoreJobInfo()
{
    if (GetJobInfo() != nullptr) {
        m_jobInfoPtr = std::dynamic_pointer_cast<AppProtect::RestoreJob>(GetJobInfo()->GetJobInfo());
    }
    if (m_jobInfoPtr == nullptr) {
        HCP_Log(ERR, MODULE) << "Failed to get backupJobPtr." << HCPENDLOG;
        ReportJobDetailsWithLabelAndErrcode(JobLogLevel::TASK_LOG_ERROR, SubJobStatus::FAILED, PROGRESS0,
            "nas_plugin_hetro_restore_data_fail_label", INITIAL_ERROR_CODE);
        return false;
    }

    SetMainJobId(m_jobInfoPtr->jobId);
    SetSubJobId();

    return true;
}

bool NasArchiveRestore::IsAbort() const
{
    return IsAbortJob() || IsJobPause();
}

int NasArchiveRestore::PrerequisiteJob()
{
    INFOLOG("Enter NasArchiveRestore PrerequisiteJob");
    m_jobCtrlPhase = JOB_CTRL_PHASE_PREJOB;
    if (!GetRestoreJobInfo()) {
        SetJobToFinish();
        return Module::FAILED;
    }
    int ret = PrerequisiteJobInner();
    INFOLOG("Report PrerequisiteJob State");
    if (ret != Module::SUCCESS) {
        ERRLOG("PrerequisiteJobInner failed");
        ReportJobDetailsWithLabelAndErrcode(JobLogLevel::TASK_LOG_ERROR, SubJobStatus::FAILED, PROGRESS100,
            "nas_plugin_hetro_restore_prepare_fail_label", INITIAL_ERROR_CODE);
        SetJobToFinish();
        return Module::FAILED;
    }

    ReportJobDetailsWithLabelAndErrcode(JobLogLevel::TASK_LOG_INFO, SubJobStatus::COMPLETED, PROGRESS100,
        "nas_plugin_hetro_restore_prepare_succeed_label", INITIAL_ERROR_CODE);
    SetJobToFinish();
    return Module::SUCCESS;
}

int NasArchiveRestore::GenerateSubJob()
{
    INFOLOG("Enter NasArchiveRestore GenerateSubJob");
    m_jobCtrlPhase = JOB_CTRL_PHASE_GENSUBJOB;
    if (!GetRestoreJobInfo()) {
        SetJobToFinish();
        return Module::FAILED;
    }

    ReportJobDetailsWithLabelAndErrcode(JobLogLevel::TASK_LOG_INFO, SubJobStatus::RUNNING, PROGRESS0,
        "nas_plugin_archive_restore_scan_start_label", INITIAL_ERROR_CODE);
    m_jobState = ArchiveJobState::RUNNING;
    int ret = GenerateSubJobInner();
    INFOLOG("Report GenerateSubJob State");
    if (ret != Module::SUCCESS) {
        ERRLOG("GenerateSubJobInner failed");
        if (m_jobState == ArchiveJobState::RUNNING) {
            m_jobState = ArchiveJobState::FAILED;
        }
    }

    ReportGenerateSubJob();
    SetJobToFinish();
    return Module::SUCCESS;
}

void NasArchiveRestore::ReportGenerateSubJob()
{
    DBGLOG("Enter ReportGenerateSubJob, totFailedDirs: %d", m_scanStats.m_totFailedDirs);
    std::string jobLabel;
    JobLogLevel::type type = JobLogLevel::type::TASK_LOG_ERROR;
    SubJobStatus::type status = SubJobStatus::FAILED;

    switch (m_jobState) {
        case ArchiveJobState::SUCCESS:
            jobLabel = "nas_plugin_hetro_restore_scan_completed_label";
            type = JobLogLevel::type::TASK_LOG_INFO;
            status = SubJobStatus::COMPLETED;
            break;
        case ArchiveJobState::FAILED:
            jobLabel = "nas_plugin_archive_restore_scan_fail_label";
            type = JobLogLevel::type::TASK_LOG_ERROR;
            status = SubJobStatus::FAILED;
            break;
        case ArchiveJobState::ABORT:
            jobLabel = "nas_plugin_archive_restore_scan_fail_label";
            type = JobLogLevel::type::TASK_LOG_WARNING;
            status = SubJobStatus::ABORTED;
            break;
        case ArchiveJobState::EMPTY_COPY:
            jobLabel = "nas_plugin_hetro_restore_scan_completed_label";
            type = JobLogLevel::type::TASK_LOG_INFO;
            status = SubJobStatus::COMPLETED;
            break;
        default:
            break;
    }
    PrintScannerStatistics(m_scanStats);
    if (m_scanStats.m_totFailedDirs != 0) {
        jobLabel = "nas_plugin_hetro_restore_scan_completed_with_warn_label";
        ReportJobDetailsWithLabelAndErrcode(type, status, PROGRESS100, jobLabel, INITIAL_ERROR_CODE,
            std::to_string(m_scanStats.m_totDirsToBackup), std::to_string(m_scanStats.m_totFilesToBackup),
            PluginUtils::FormatCapacity(m_scanStats.m_totalSizeToBackup));
    } else {
        ReportJobDetailsWithLabelAndErrcode(type, status, PROGRESS100, jobLabel, INITIAL_ERROR_CODE,
            std::to_string(m_scanStats.m_totDirsToBackup), std::to_string(m_scanStats.m_totFilesToBackup),
            PluginUtils::FormatCapacity(m_scanStats.m_totalSizeToBackup), std::to_string(m_scanStats.m_totDirsToBackup),
            std::to_string(m_scanStats.m_totFilesToBackup),
            PluginUtils::FormatCapacity(m_scanStats.m_totalSizeToBackup));
    }
    DBGLOG("Exit ReportScannerCompleteStatus");
}

int NasArchiveRestore::ExecuteSubJob()
{
    INFOLOG("Enter NasArchiveRestore ExecuteSubJob");
    AddSubJobToChannelMgr();
    std::shared_ptr<void> delSubJob(nullptr, [&](...) {
        RemoveSubJobFromChannelMgr();
    });
    m_jobCtrlPhase = JOB_CTRL_PHASE_EXECSUBJOB;
    if (!GetRestoreJobInfo()) {
        SetJobToFinish();
        return Module::FAILED;
    }
    ReportJobDetails(JobLogLevel::TASK_LOG_INFO, SubJobStatus::RUNNING, PROGRESS0);
    int ret = ExecuteSubJobInner();
    ShareResourceManager::GetInstance().RemoveResourceCache(ShareResourceType::BACKUP, m_subJobId);
    INFOLOG("Report ExecuteSubJobInner State");
    if (ret != Module::SUCCESS && m_jobState != ArchiveJobState::SKIP_PHASE) {
        ERRLOG("ExecuteSubJobInner failed");
        ReportJobDetailsWithLabelAndErrcode(JobLogLevel::TASK_LOG_ERROR, SubJobStatus::FAILED, PROGRESS0,
            "nas_plugin_archive_restore_data_fail_label", INITIAL_ERROR_CODE);
        SetJobToFinish();
        return Module::FAILED;
    }

    ReportJobDetails(JobLogLevel::TASK_LOG_INFO, SubJobStatus::COMPLETED, PROGRESS100);
    SetJobToFinish();
    return Module::SUCCESS;
}

int NasArchiveRestore::PostJob()
{
    INFOLOG("Enter NasArchiveRestore PostJob");
    m_jobCtrlPhase = JOB_CTRL_PHASE_POSTJOB;
    if (!GetRestoreJobInfo()) {
        SetJobToFinish();
        return MP_FAILED;
    }
    AddSubJobToChannelMgr();
    std::shared_ptr<void> delSubJob(nullptr, [&](...) {
        RemoveSubJobFromChannelMgr();
    });
    int ret = PostJobInner();
    INFOLOG("Report PostJob State");
    CalcuMainBackupStats(m_backupStats);
    if (ret != Module::SUCCESS) {
        ERRLOG("PostJob failed");
        m_jobState = ArchiveJobState::FAILED;
    } else if (m_backupStats.m_noOfDirFailed != 0 || m_backupStats.m_noOfFilesFailed != 0) {
        m_jobState = ArchiveJobState::PARTIAL_SUCCESS;
    } else {
        m_jobState = ArchiveJobState::SUCCESS;
    }
    ReportPostJob();
    DeleteSharedResources();
    SetJobToFinish();
    return Module::SUCCESS;
}

void NasArchiveRestore::ReportPostJob()
{
    std::string jobLabel;
    SubJobStatus::type status = SubJobStatus::FAILED;
    JobLogLevel::type type = JobLogLevel::type::TASK_LOG_ERROR;
    switch (m_jobState) {
        case ArchiveJobState::SUCCESS:
            jobLabel = "nas_plugin_hetro_restore_data_completed_label";
            type = JobLogLevel::type::TASK_LOG_INFO;
            status = SubJobStatus::COMPLETED;
            break;
        case ArchiveJobState::PARTIAL_SUCCESS:
            jobLabel = "nas_plugin_hetro_restore_data_completed_with_warn_label";
            type = JobLogLevel::type::TASK_LOG_WARNING;
            status = SubJobStatus::FAILED;
            break;
        case ArchiveJobState::FAILED:
            jobLabel = "";
            type = JobLogLevel::type::TASK_LOG_ERROR;
            status = SubJobStatus::FAILED;
            break;
        default:
            break;
    }
    ReportJobDetailsWithLabelAndErrcode(type, status, PROGRESS100, jobLabel, INITIAL_ERROR_CODE,
                                        std::to_string(m_backupStats.m_noOfDirCopied),
                                        std::to_string(m_backupStats.m_noOfFilesCopied),
                                        PluginUtils::FormatCapacity(m_backupStats.m_noOfBytesCopied),
                                        std::to_string(m_backupStats.m_noOfDirFailed),
                                        std::to_string(m_backupStats.m_noOfFilesFailed));
}

bool NasArchiveRestore::InitJobInfo()
{
    INFOLOG("Enter InitJobInfo");
    m_jobInfoPtr = std::dynamic_pointer_cast<AppProtect::RestoreJob>(m_jobCommonInfo->GetJobInfo());
    m_jobId = m_jobInfoPtr->jobId;
    m_subJobId = (m_subJobInfo != nullptr) ? m_subJobInfo->subJobId : m_jobId;
    INFOLOG("Init job info, job id: %s, %s", m_jobId.c_str(), m_subJobId.c_str());

    if (m_jobInfoPtr->copies.empty()) {
        ERRLOG("Job copies is empty.");
        return false;
    }

    try {
        SetArchiveSourceInfo();
    } catch (const std::invalid_argument &e) {
        WARNLOG("SetArchiveSourceInfo failed: %s", e.what());
        return false;
    }

    m_isAggCopy = (m_jobInfoPtr->copies[0].formatType == CopyFormatType::type::INNER_DIRECTORY) ? true : false;
    INFOLOG("Copy form is %s", m_isAggCopy ? "aggregate" : "normal");

    return true;
}

bool NasArchiveRestore::GetBackupCopyInfo(const std::string& backupCopyInfo)
{
    Json::Value value;
    if (!Module::JsonHelper::JsonStringToJsonValue(backupCopyInfo, value)) {
        ERRLOG("Convert backupCopyInfo json string to json value failed.");
        return false;
    }

    Json::Value aggCopyExtValue = value["extendInfo"];
    if (!Module::JsonHelper::JsonValueToStruct(aggCopyExtValue, m_aggCopyInfo)) {
        ERRLOG("JsonStringToStruct failed, aggCopyExtendInfo");
        return false;
    }
    INFOLOG("GetBackupCopyInfo success");
    return true;
}

// Get repo info from RestoreJob
/** cache
* | -- CacheFsPath                      // CacheFsPath from UBC --> m_cacheFsPath
*    | -- source_policy                 // download meta from archive m_cacheMdPath
*        | -- filemeta                  // m_downloadMetePath
*           |-- metefile.zip
*        | -- latest                    // metefile.zip unzip in sharecopy or copy in fscopy -> m_scanInputMetePath
*        | -- sqlite                    // index file
*    | -- scan_ctrl             // m_scanControlFilePath
*    | -- backup_ctrl           // m_backupControlFilePath
*    | -- statistics                    // Folder to save the statistic of backup main job and sub-jobs
*/
bool NasArchiveRestore::InitRepoInfo()
{
    INFOLOG("Enter InitRepoInfo");
    StorageRepository cacheFs;
    for (unsigned int i = 0; i < m_jobInfoPtr->copies[0].repositories.size(); i++) {
        if (m_jobInfoPtr->copies[0].repositories[i].repositoryType == RepositoryDataType::CACHE_REPOSITORY) {
            cacheFs = m_jobInfoPtr->copies[0].repositories[i];
        }
    }

    if (cacheFs.path.empty()) {
        ERRLOG("Cache repo path is empty");
        return false;
    }
    m_cacheFsPath = cacheFs.path[0];
    INFOLOG("Cache repo remote path: %s", cacheFs.remotePath.c_str());
    INFOLOG("Archive restore cache repo path: ", m_cacheFsPath.c_str());

    if (!PluginUtils::IsDirExist(m_cacheFsPath)) {
        ERRLOG("m_cacheFsPath path no exist: %s", m_cacheFsPath.c_str());
        return false;
    }

    std::string backupCopyInfo =  m_jobInfoPtr->copies[0].extendInfo;
    INFOLOG("Backup copy info: %s", Module::WipeSensitiveDataForLog(backupCopyInfo).c_str());

    if (IsNasShareCopy()) {
        if (!m_isAggCopy) {
            m_cacheMdPath =  m_cacheFsPath + "/" + m_sourceContextMd;
            m_dataPath = "/" + m_sourceContext;
        } else {
            GetBackupCopyInfo(backupCopyInfo);
            m_cacheMdPath =  m_cacheFsPath + "/" + m_sourceContextMd;
            m_dataPath = "/" + m_aggCopyInfo.metaPathSuffix;
        }
    } else if (IsFileSystemCopy()) {
        m_cacheMdPath =  m_cacheFsPath + "/" + m_sourceContextMd; // 同构也放在md目录下
        m_dataPath = ""; // 同构目标s3没有md层级
    } else {
        ERRLOG("No available copy type");
        return false;
    }

    m_downloadMetePath = m_cacheMdPath + "/filemeta";
    m_scanInputMetePath = m_cacheMdPath + "/latest";
    m_scanControlFilePath = m_cacheFsPath + "/" + "scan_ctrl";
    m_backupControlFilePath = m_cacheFsPath + "/" + "backup_ctrl";
    m_statsPath = m_cacheFsPath + "/" + "statistics";
    return true;
}

bool NasArchiveRestore::InitRestoreInfo()
{
    INFOLOG("Enter InitRestoreInfo");
    // get restore root path
    std::string extJsonString = m_jobInfoPtr->targetObject.extendInfo;
    INFOLOG("Extend info json string: %s", Module::WipeSensitiveDataForLog(extJsonString).c_str());
    FileSetInfo fileSetInfo;
    if (!Module::JsonHelper::JsonStringToStruct(extJsonString, fileSetInfo)) {
        ERRLOG("Convert to FileSetInfo failed.");
        return false;
    }

    // make file list for fine restore
    if (IsFineRestore()) {
        for (const auto& info : m_jobInfoPtr->restoreSubObjects) {
            std::string name = info.name;
            if (!name.empty()) {
                m_fineRestoreObj.push_back(name);
                DBGLOG("Restore src path: %s", name.c_str());
            }
        }
    }
    return true;
}

bool NasArchiveRestore::InitInfo()
{
    INFOLOG("Enter InitInfo");
    if (!InitJobInfo()) {
        ERRLOG("Init job info failed.");
        return false;
    }
    if (!InitRepoInfo()) {
        ERRLOG("Init repo info failed.");
        return false;
    }
    if (!InitRestoreInfo()) {
        ERRLOG("Init restore info failed.");
        return false;
    }
    if (!InitArchiveInfo()) {
        ERRLOG("Init archive info failed.");
        return false;
    }
    // 把归档服务器ip加入到路由中
    m_IpRuleList.insert(m_IpRuleList.end(), m_archiveServerInfo.ipList.begin(),
        m_archiveServerInfo.ipList.end());
    
    if (!InitProtocolInfo()) {
        ERRLOG("Init protocol info failed.");
        return false;
    }
    PrintJobInfo();
    return true;
}

bool NasArchiveRestore::InitArchiveInfo()
{
    INFOLOG("Enter InitArchiveInfo");
    std::string copyExtInfoString {};
    for (const auto& copy : m_jobInfoPtr->copies) {
        for (const auto& repo : copy.repositories) {
            if (repo.protocol == RepositoryProtocolType::type::S3 ||
                repo.protocol == RepositoryProtocolType::type::TAPE) {
                copyExtInfoString = repo.extendInfo;
            }
            if (repo.protocol == RepositoryProtocolType::type::TAPE) {
                m_isTapeRestore = true;
            }
        }
    }
    if (copyExtInfoString.empty()) {
        ERRLOG("Copy extend info of archive is empty.");
        return false;
    }

    ExtendArchiveInfo extendArchiveInfo {};
    if (!Module::JsonHelper::JsonStringToStruct(copyExtInfoString, extendArchiveInfo)) {
        ERRLOG("Convert archive server json string to struct failed");
        return false;
    }

    if (extendArchiveInfo.serviceInfo.empty()) {
        ERRLOG("Archive server service info is empty");
        return false;
    }

    m_archiveServerInfo.port = extendArchiveInfo.serviceInfo[0].port;
    m_archiveServerInfo.enableSSL = extendArchiveInfo.enableSSL;
    for (const auto& info : extendArchiveInfo.serviceInfo) {
        m_archiveServerInfo.ipList.push_back(info.ip);
    }

    return true;
}

bool NasArchiveRestore::InitProtocolInfo()
{
    // 文件系统恢复or共享恢复
    if (m_jobInfoPtr->targetObject.subType == "NasFileSystem") {
        if (!InitRestoreInfoFileSystem()) {
            HCP_Log(ERR, MODULE) << "InitRestoreInfoFileSystem failed" << HCPENDLOG;
            return false;
        }
    } else if (m_jobInfoPtr->targetObject.subType == "NasShare") {
        if (!InitRestoreInfoNasShare()) {
            HCP_Log(ERR, MODULE) << "InitRestoreInfoNasShare failed" << HCPENDLOG;
            return false;
        }
    }
    if (m_nasShare.protocol.empty()) {
        HCP_Log(ERR, MODULE) << "m_nasShare.protocol is empty: " << m_nasShare.protocol << HCPENDLOG;
        return false;
    }
    return true;
}

/* 检查连通性 */
bool NasArchiveRestore::CheckConnect()
{
    // kerberos
    if (IsRestoreByCIFS() && IsUseKerberos()) {
        if (!InitKerberos()) {
            HCP_Log(ERR, MODULE) << "InitKerberos failed" << HCPENDLOG;
            return false;
        }
    }

    if (!CheckAvailableShareInfo()) {
        HCP_Log(ERR, MODULE) << "CheckAvailableShareInfo failed" << HCPENDLOG;
        return false;
    }
    return true;
}

bool NasArchiveRestore::CheckAvailableShareInfo()
{
    if (IsRestoreByNFS()) {
        if (!CheckConnectNFS(NFS_VERSION_3)) {
            ERRLOG("Nas share is not reachable with specified version");
            return false;
        }
        return true;
    }

    if (IsRestoreByCIFS()) {
        if (!GetAvailableIpSMB()) {
            ERRLOG("GetAvailableIpSMB failed");
            return false;
        }
        return true;
    }

    HCP_Log(ERR, MODULE) << "MountRemoteNasShare failed, not available protocol type" << HCPENDLOG;
    return false;
}

bool NasArchiveRestore::GetAvailableIpSMB()
{
    std::string protoVersion;

    for (const auto& version : { Module::SmbVersion::VERSION0302, Module::SmbVersion::VERSION0300,
        Module::SmbVersion::VERSION0210, Module::SmbVersion::VERSION0202 }) {
        if (CheckConnectSMB(m_nasShare.ip, version)) {
            protoVersion = ConvertSmbVersionToString(version);
            break;
        }
    }
    if (protoVersion.empty()) {
        ERRLOG("No available ip or proto version");
        return false;
    }
    m_nasShare.protocolVersion = protoVersion;
    INFOLOG("NAS share ip: %s, protocol version: %s", m_nasShare.ip.c_str(), protoVersion.c_str());
    return true;
}

bool NasArchiveRestore::CheckConnectSMB(const std::string& ip, Module::SmbVersion version)
{
    INFOLOG("CheckConnectSMB ip: %s, version: %d", ip.c_str(), version);
    std::string agentHomePath = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    std::string krb5CcacheFile = agentHomePath + KRB5CCNAMEPREFIX + m_jobId;
    std::string krb5ConfigFile = agentHomePath + KRB5CONFIGPREFIX + m_jobId + KRB5CONFIGPOSTFIX;

    Module::SmbAuthType smbRmtAuthType;
    if (m_nasShare.auth.authType == AuthType::type::KERBEROS) {
        smbRmtAuthType = Module::SmbAuthType::KRB5;
    } else if (m_nasShare.auth.authType == AuthType::type::NO_AUTHENTICATION ||
        m_nasShare.auth.authType == AuthType::type::OS_PASSWORD ||
        m_nasShare.auth.authType == AuthType::type::APP_PASSWORD) {
        smbRmtAuthType = Module::SmbAuthType::NTLMSSP;
    } else {
        HCP_Log(INFO, MODULE) << "Wrong authType for cifs share: " << m_nasShare.auth.authType << HCPENDLOG;
        return false;
    }
    bool smbEncryption = m_nasShare.encryption == SMB_ENCRYPTION;
    Module::SmbContextArgs rmtPars = {
        m_nasShare.domainName, ip, m_nasShare.sharePath,
        m_nasShare.auth.authkey, m_nasShare.auth.authPwd, krb5CcacheFile,
        krb5ConfigFile, smbEncryption, false, ONE_MINUTE, smbRmtAuthType, version
    };
    Module::SmbContextWrapper rmtSmb(rmtPars);
    if (!rmtSmb.Init() || !rmtSmb.SmbConnect()) {
        ERRLOG("SMB connect failed");
        return false;
    }
    uint64_t readSize = rmtSmb.SmbGetMaxReadSize();
    uint64_t writeSize = rmtSmb.SmbGetMaxWriteSize();
    m_nasShare.blockSize = std::min(readSize, writeSize);
    INFOLOG("Read size: %llu, write size: %llu, max block size: %u", readSize, writeSize, m_nasShare.blockSize);

    return true;
}

bool NasArchiveRestore::CheckConnectNFS(const std::string& protoVersion)
{
    std::string nasMountOptions = NFS_MOUNT_OPTION;
    std::string tempLocalMountPath = "/mnt/" + m_subJobId;
    std::string sharePath = m_nasShare.sharePath;

    std::string outSelectSvcIp;
    PluginUtils::NasMountParams mountParams = {
        "NFS", protoVersion, m_nasShare.sharePath, tempLocalMountPath, nasMountOptions,
        m_nasShare.auth.authkey, m_nasShare.auth.authPwd, {m_nasShare.ip}};
    // outSelectSvcIp 可能有多个，这里取一个有效的
    if (PluginUtils::MountNFS(mountParams, outSelectSvcIp) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Mount remote nas failed for share: " << sharePath << HCPENDLOG;
        return false;
    }
    if (PluginUtils::UnmountNas(tempLocalMountPath) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Unmount remote nas failed for share: " << sharePath << HCPENDLOG;
        return false;
    }
    m_nasShare.protocolVersion = NFS_VERSION_3;
    HCP_Log(INFO, MODULE) << "m_nasShare.nasShareExt.m_serviceIP: " << m_nasShare.ip << HCPENDLOG;

    return true;
}

void NasArchiveRestore::PrintJobInfo() const
{
    HCP_Log(INFO, MODULE) << "jobPhase: " << m_jobCtrlPhase << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "jobId: " << m_jobId << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "subJobId: " << m_subJobId << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "m_copyId: " << m_copyId << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "nasShare.id: " << m_nasShare.id << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "qos.bandwidth: " << m_jobInfoPtr->jobParam.qos.bandwidth << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "qos.protectIops: " << m_jobInfoPtr->jobParam.qos.protectIops << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "qos.backupIops: " << m_jobInfoPtr->jobParam.qos.backupIops << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "InitJobInfo m_nasShare.ip: " << m_nasShare.ip << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "InitJobInfo m_nasShare.sharePath: " << m_nasShare.sharePath << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "InitJobInfo m_coveragePolicy: " << static_cast<int>(m_coveragePolicy) << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "InitJobInfo m_fineRestorePath: " << m_fineRestorePath << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "InitJobInfo m_nasShare.protocol: " << m_nasShare.protocol << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "InitJobInfo m_nasShare.user: " << WIPE_SENSITIVE(m_nasShare.user) << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "InitJobInfo m_nasShare.auth.authType: " << m_nasShare.auth.authType << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "InitJobInfo m_restoreLocationType: " << m_restoreLocationType << HCPENDLOG;
}

inline bool NasArchiveRestore::IsFileSystemCopy() const
{
    return m_protectSubtype == "NasFileSystem";
}

inline bool NasArchiveRestore::IsNasShareCopy() const
{
    return m_protectSubtype == "NasShare";
}

inline bool NasArchiveRestore::IsRestoreByCIFS() const
{
    return m_nasShare.protocol == SMB_PROTOCOL;
}

inline bool NasArchiveRestore::IsRestoreByNFS() const
{
    return m_nasShare.protocol == NFS_PROTOCOL;
}

inline bool NasArchiveRestore::IsUseKerberos() const
{
    return m_nasShare.auth.authType == AuthType::type::KERBEROS;
}

bool NasArchiveRestore::InitRestoreInfoFileSystem()
{
    HCP_Log(INFO, MODULE) << "Enter InitRestoreInfoFileSystem" << HCPENDLOG;
    if (!Module::JsonHelper::JsonStringToStruct(m_jobInfoPtr->extendInfo, m_fileSystemSourceInfo)) {
        HCP_Log(ERR, MODULE) << "Init datalayout info failed" << HCPENDLOG;
        return false;
    }
    ResolveDomain(m_fileSystemSourceInfo.shareIp, m_remoteIpRuleList);               // ip
    m_nasShare.sharePath = *(m_fileSystemSourceInfo.sharePath.begin()) == '/' ?  m_fileSystemSourceInfo.sharePath :
        "/" + m_fileSystemSourceInfo.sharePath;                                 // path
    m_fineRestorePath = m_fileSystemSourceInfo.targetPath;                  // 细粒度恢复目标路径
    m_restoreLocationType = m_fileSystemSourceInfo.targetLocation;          // 恢复位置
    m_nasShare.protocol = m_fileSystemSourceInfo.protocol;                   // 协议类别
    m_nasShare.auth = m_jobInfoPtr->targetObject.auth;
    m_nasShare.user = m_jobInfoPtr->targetObject.auth.authkey;                 // cifs
    m_nasShare.password = m_jobInfoPtr->targetObject.auth.authPwd;             // cifs
    HCP_Log(INFO, MODULE) << "m_nasShare.auth.authkey: " << WIPE_SENSITIVE(m_nasShare.auth.authkey) << HCPENDLOG;
    if (m_fileSystemSourceInfo.targetLocation == FILELEVELRESTORENATIVE) {  //   本机位置用remoteHost里的ip
        if (!FillIplistForNativeRestore()) {
            return false;
        }
    }
    IdentifyFileReplaceStrategy(m_fileSystemSourceInfo.fileReplaceStrategy); // 0 覆盖 // 1 跳过 // 2 比较
    return true;
}

bool NasArchiveRestore::InitRestoreInfoNasShare()
{
    HCP_Log(INFO, MODULE) << "Enter InitRestoreInfoNasShare" << HCPENDLOG;
    if (!Module::JsonHelper::JsonStringToStruct(m_jobInfoPtr->targetObject.extendInfo, m_shareSourceInfo)) {
        HCP_Log(ERR, MODULE) << "Init datalayout info failed" << HCPENDLOG;
        return false;
    }
    if (!Module::JsonHelper::JsonStringToStruct(m_jobInfoPtr->extendInfo, m_strategyShare)) {
        HCP_Log(ERR, MODULE) << "Init datalayout info failed" << HCPENDLOG;
        return false;
    }
    ResolveDomain(m_shareSourceInfo.shareIp, m_remoteIpRuleList);               // ip
    m_nasShare.domainName = m_shareSourceInfo.domainName;                      // domainName
    m_nasShare.protocol = m_shareSourceInfo.shareMode;                         // 协议类别
    m_nasShare.encryption = m_shareSourceInfo.encryption;                      // 协议类别
    m_nasShare.sharePath = *(m_jobInfoPtr->targetObject.name.begin()) == '/' ?  m_jobInfoPtr->targetObject.name :
        "/" + m_jobInfoPtr->targetObject.name;                                 // path
    m_fineRestorePath = m_strategyShare.targetPath;                            // 细粒度恢复目标路径
    m_restoreLocationType = m_strategyShare.targetLocation;                    // 恢复位置
    m_nasShare.auth = m_jobInfoPtr->targetObject.auth;
    m_nasShare.user = m_jobInfoPtr->targetObject.auth.authkey;                 // cifs
    m_nasShare.password = m_jobInfoPtr->targetObject.auth.authPwd;             // cifs
    HCP_Log(INFO, MODULE) << "m_nasShare.auth.authkey: " << WIPE_SENSITIVE(m_nasShare.auth.authkey) << HCPENDLOG;
    if (m_strategyShare.targetLocation == FILELEVELRESTORENATIVE) {            // 本机位置用remoteHost里的ip
        if (!FillIplistForNativeRestore()) {
            return false;
        }
        m_nasShare.sharePath = *(m_strategyShare.sharePath.begin()) == '/' ? m_strategyShare.sharePath :
            "/" + m_strategyShare.sharePath;
    }
    IdentifyFileReplaceStrategy(m_strategyShare.fileReplaceStrategy);          // 0 覆盖 // 1 跳过 // 2 比较
    return true;
}

// 细粒度本机位置从data仓获取本地172  IP以及cifs鉴权信息
bool NasArchiveRestore::FillIplistForNativeRestore()
{
    HCP_Log(INFO, MODULE) << "Enter fill ip list for Native restore" << HCPENDLOG;
    if (m_jobInfoPtr->copies.empty()) {
        HCP_Log(ERR, MODULE) << "m_jobInfoPtr->copies is empty" << HCPENDLOG;
        return false;
    }

    for (const auto& repo : m_jobInfoPtr->copies[0].repositories) {
        if (repo.repositoryType == RepositoryDataType::type::DATA_REPOSITORY) {
            for (const auto& hostAddress : repo.remoteHost) {
                HCP_Log(INFO, MODULE) << "insert ip to iplist : " << hostAddress.ip << HCPENDLOG;
                m_remoteIpRuleList.push_back(hostAddress.ip);
            }
            m_nasShare.auth = repo.auth;
            m_nasShare.user = repo.auth.authkey;
            m_nasShare.password = repo.auth.authPwd;
        }
    }
    m_nasShare.auth.authType = AuthType::type::APP_PASSWORD;
    HCP_Log(INFO, MODULE) << "Native restore authkey: " << WIPE_SENSITIVE(m_nasShare.auth.authkey) << HCPENDLOG;
    return true;
}

bool NasArchiveRestore::InitKerberos()
{
    HCP_Log(INFO, MODULE) << "Enter InitKerberos" << HCPENDLOG;
    if (!Module::JsonHelper::JsonStringToStruct(
        m_jobInfoPtr->targetObject.auth.extendInfo, m_nasShare.nasShareAuthExt)) {
        HCP_Log(ERR, MODULE) << "InitKerberosInfo JsonStringToStruct failed." << HCPENDLOG;
        return false;
    }
    /* set krb5 environment variable for krb5.conf */
    if (PluginUtils::KinitTGTInner(m_nasShare.auth.authkey, m_nasShare.nasShareAuthExt.secret,
        m_nasShare.nasShareAuthExt.keytab,
        m_nasShare.nasShareAuthExt.krb5Conf, m_jobInfoPtr->jobId) != Module::SUCCESS) {
        HCP_Log(ERR, MODULE) << "InitKerberos FAILED." << HCPENDLOG;
        return false;
    }
    HCP_Log(INFO, MODULE) << "InitKerberos SUCCESS." << HCPENDLOG;
    return true;
}

void NasArchiveRestore::PrintInfo() const
{
    INFOLOG("m_cacheMdPath: %s", m_cacheMdPath.c_str());
    INFOLOG("m_dataPath: %s", m_dataPath.c_str());
    INFOLOG("m_downloadMetePath: %s", m_downloadMetePath.c_str());
    INFOLOG("m_scanInputMetePath: %s", m_scanInputMetePath.c_str());
    INFOLOG("m_scanControlFilePath: %s", m_scanControlFilePath.c_str());
    INFOLOG("m_backupControlFilePath: %s", m_backupControlFilePath.c_str());
    INFOLOG("m_statsPath: %s", m_statsPath.c_str());
}

bool NasArchiveRestore::IsFineRestore() const
{
    return m_jobInfoPtr->jobParam.restoreType == RestoreJobType::FINE_GRAINED_RESTORE;
}

bool NasArchiveRestore::InitArchiveClient()
{
    DBGLOG("Enter InitArchiveClinet");
    m_archiveClient = std::make_shared<ArchiveClient>(m_jobId, m_copyId);
    const auto& [ipList, port, ssl] = m_archiveServerInfo;
    if (m_archiveClient->InitClient(ipList, port, ssl) != Module::SUCCESS) {
        ERRLOG("Archive client connect to server failed.");
        return false;
    }
    DBGLOG("Client Connect success");
    return true;
}

int NasArchiveRestore::PrerequisiteJobInner()
{
    INFOLOG("Enter PrerequisiteJobInner");
    if (!InitInfo()) {
        return Module::FAILED;
    }
    PrintInfo();
    // 添加路由
    if (!OperateIpsRule(m_IpRuleList, "ADD") ||
        !OperateIpsRuleForRemoteIp(m_remoteIpRuleList, "ADD", m_nasShare.ip)) {
        ERRLOG("add ip rule failed");
        return Module::FAILED;
    }
    if (!CheckConnect()) {
        ERRLOG("CheckConnect Failed!");
        return Module::FAILED;
    }
    // test connect to archive server
    if (!InitArchiveClient()) {
        return Module::FAILED;
    }

    if (m_archiveClient->DisConnect() != Module::SUCCESS) {
        ERRLOG("Check disconnect archive server failed.");
        return Module::FAILED;
    }
    INFOLOG("Exit PrerequisiteJobInner");
    return Module::SUCCESS;
}

int NasArchiveRestore::GenerateSubJobInner()
{
    INFOLOG("Enter GenerateSubJobInner");

    if (!InitInfo() || !OperateIpsRule(m_IpRuleList, "ADD") ||
        !OperateIpsRuleForRemoteIp(m_remoteIpRuleList, "ADD", m_nasShare.ip)) {
        return Module::FAILED;
    }

    if (!InitCacheRepoDir()) {
        return Module::FAILED;
    }
    PrintInfo();

    // parase csv download dchache/fcahce/metafile no need to copy to cache
    if (!DownloadMetaFile()) {
        if (m_jobState == ArchiveJobState::EMPTY_COPY) {
            // 空副本不需要恢复，直接认为成功
            INFOLOG("This is empty copy.");
            return Module::SUCCESS;
        }
        ERRLOG("Download meta file from archive server failed.");
        ReportJobDetailsWithLabelAndErrcode(JobLogLevel::TASK_LOG_ERROR, SubJobStatus::FAILED, PROGRESS100,
            "nas_plugin_archive_restore_download_metafile_fail_label", INITIAL_ERROR_CODE);
        return Module::FAILED;
    }

    // 由于E系列的下载路径是用归档文件中的参数拼接所以不带source_policy，在解压时需要替换
    if (!m_parentDir.empty()) {
        m_downloadMetePath =  m_cacheFsPath + "/Context_Global_MD/filemeta";
        std::string backCopyMetaFile = m_cacheFsPath + "/Context_Global_MD/backup-copy-meta.json";
        PluginUtils::CopyFile(backCopyMetaFile, m_cacheMdPath + "/backup-copy-meta.json");
    }

    auto ret = std::async(std::launch::async, std::bind(&NasArchiveRestore::PrepareMetaFile, this));
    while (!m_isPrepareMetaFinish) {
        ReportJobDetails(JobLogLevel::TASK_LOG_INFO, SubJobStatus::RUNNING, PROGRESS0);
        std::this_thread::sleep_for(std::chrono::seconds(REPORT_MONITOR_TIME));
    }
    if (!ret.get()) {
        ERRLOG("PrepareMetaFile failed");
        return Module::FAILED;
    }

    if (IsAbortJob()) {
        ERRLOG("Job aborted, skip scanner.");
        return Module::FAILED;
    }

    if (!StartScanner()) {
        ERRLOG("Start Scanner Failed");
        return Module::FAILED;
    }

    MonitorScan();
    m_scanner->Destroy();
    CreateCheckSubTask();
    INFOLOG("Exit GenerateSubJobInner");
    return Module::SUCCESS;
}

// nas共享格式使用下载的备份的metafile，文件系统格式使用归档扫描提供的metafile
bool NasArchiveRestore::PrepareMetaFile()
{
    std::shared_ptr<void> defer(nullptr, [&](...) { m_isPrepareMetaFinish = true; });
    if (IsNasShareCopy()) {
        if (!UncompressMetafile()) {
            ERRLOG("UncompressMetafile failed");
            return false;
        }
    } else if (IsFileSystemCopy()) {
        if (!CopyMetaFile()) {
            ERRLOG("CopyMetaFile failed");
            return false;
        }
    } else {
        ERRLOG("Copy type is unknow");
        return false;
    }
    return true;
}

bool NasArchiveRestore::CopyMetaFile() const
{
    INFOLOG("ScanInputMetePath: %s", m_scanInputMetePath.c_str());
    if (!PluginUtils::CreateDirectory(m_scanInputMetePath)) {
        ERRLOG("Create directory failed: %s", m_scanInputMetePath.c_str());
        return false;
    }

    // 拷贝归档提供的meta文件到cache仓
    std::string cmd = "cp -ra " + m_fileSystemMetaPath + "/" + m_archiveFsId + "/* " + m_scanInputMetePath;
    std::vector<std::string> output;
    std::vector<std::string> errOutput;
    int ret = Module::runShellCmdWithOutput(INFO, "NasArchiveRestore", 0, cmd, {}, output, errOutput);
    if (ret != 0) {
        ERRLOG("copy failed: %s, ret: %d", cmd.c_str(), ret);
        std::for_each(output.begin(), output.end(),
            [&] (const std::string& v) { ERRLOG("output: %s", v.c_str());});
        std::for_each(errOutput.begin(), errOutput.end(),
            [&] (const std::string& v) { ERRLOG("output: %s", v.c_str());});
        return false;
    }
    INFOLOG("CopyMetaFile success");
    return true;
}

bool NasArchiveRestore::InitCacheRepoDir()
{
    if (!PluginUtils::RecurseCreateDirectory(m_cacheMdPath)) {
        ERRLOG("m_cacheMdPath path create failed: %s", m_cacheMdPath.c_str());
        return false;
    }
    if (!PluginUtils::RecurseCreateDirectory(m_scanControlFilePath)) {
        ERRLOG("m_scanControlFilePath path create failed: %s", m_scanControlFilePath.c_str());
        return false;
    }
    if (!PluginUtils::RecurseCreateDirectory(m_backupControlFilePath)) {
        ERRLOG("m_backupControlFilePath path create failed: %s", m_backupControlFilePath.c_str());
        return false;
    }
    if (!PluginUtils::RecurseCreateDirectory(m_statsPath)) {
        ERRLOG("m_statsPath path create failed: %s", m_statsPath.c_str());
        return false;
    }

    if (!InitMainResources()) {
        ERRLOG("InitMainResources failed");
        return false;
    }

    if (!QueryMainScanResources()) {
        ERRLOG("QueryMainScanResources failed");
        return false;
    }
    return true;
}

bool NasArchiveRestore::SendJobReportForAliveness()
{
    int64_t currTime = PluginUtils::GetCurrentTimeInSeconds();
    if ((currTime - m_lastJobReportTime) > REPORT_INTERVAL) {
        ReportJobDetails(JobLogLevel::TASK_LOG_INFO, SubJobStatus::RUNNING, PROGRESS0);
        m_lastJobReportTime = PluginUtils::GetCurrentTimeInSeconds();
    }
    return true;
}

bool NasArchiveRestore::DownloadMetaFile()
{
    INFOLOG("Enter DownloadCacheFile");
    std::vector<std::string> pathList;
    if (IsNasShareCopy()) {
        pathList = {"/" + m_sourceContextMd + "/"};
    }
    ArchiveDownloadFile downloadFile(m_jobId, m_copyId, m_archiveServerInfo, IsNasShareCopy());

    std::thread downloadThread(std::bind(&ArchiveDownloadFile::Start,
        &downloadFile, m_cacheFsPath, pathList));
    
    while (downloadFile.m_state == ArchiveDownloadState::RUNNING) {
        if (IsAbort()) {
            WARNLOG("Abort ArchiveDownloadFile");
            downloadFile.SetAbort();
        }
        DBGLOG("Archive Downloading...");
        ReportJobDetails(JobLogLevel::TASK_LOG_INFO, SubJobStatus::RUNNING, PROGRESS0);
        std::this_thread::sleep_for(std::chrono::seconds(REPORT_MONITOR_TIME));
    }
    downloadThread.join();

    if (downloadFile.m_state == ArchiveDownloadState::FAILED) {
        ERRLOG("download failed");
        return false;
    } else if (downloadFile.m_state == ArchiveDownloadState::EMPTY_COPY) {
        m_jobState = ArchiveJobState::EMPTY_COPY;
        WARNLOG("Empty archive copy");
        return false;
    } else if (downloadFile.m_state == ArchiveDownloadState::TAP_REMIND) {
        ReportJobDetailsWithLabelAndErrcode(
            JobLogLevel::TASK_LOG_ERROR, SubJobStatus::RUNNING, PROGRESS0,
            "plugin_tape_not_support_direct_restore_label", 0);
        return false;
    }

    // get archive fs id in S3
    m_archiveFsId = downloadFile.GetFileSystemsId();
    m_fileSystemMetaPath = downloadFile.GetMetaFilePath();
    m_parentDir = downloadFile.GetParentDir();
    INFOLOG("Archive download success archiveFsId: %s, fsMetaPath: %s, m_parentDir: %s",
        m_archiveFsId.c_str(), m_fileSystemMetaPath.c_str(), m_parentDir.c_str());

    return true;
}

// unzip old "metafile.zip"
bool NasArchiveRestore::UnzipOldMetafileZip(const std::string& oldMetafileZipPath) const
{
    INFOLOG("Start to unzip old metafile.zip");
    std::vector<std::string> output;
    std::vector<std::string> errOutput;
    std::string cmd = "unzip -o -d " + m_scanInputMetePath + " " + oldMetafileZipPath;
    if (Module::runShellCmdWithOutput(INFO, "NasArchiveRestore", 0, cmd, {}, output, errOutput) != Module::SUCCESS) {
        ERRLOG("unzip failed, cmd: %s", cmd.c_str());
        std::for_each(output.begin(), output.end(),
            [&] (const std::string& v) { ERRLOG("output: %s", v.c_str());});
        std::for_each(errOutput.begin(), errOutput.end(),
            [&] (const std::string& v) { ERRLOG("output: %s", v.c_str());});
        return false;
    }
    INFOLOG("Unzip old filemeta.zip finished");
    return true;
}

// unzip new meta files (metafile_DIRCACHE.gz metafile_FILECACHE.gz metafile_META.gz metafile_XMETA.gz)
bool NasArchiveRestore::UnzipNewMetaGZipFiles() const
{
    std::vector<std::string> output;
    std::vector<std::string> errOutput;

    std::string dirCacheZipFileName = m_downloadMetePath + "/metafile_DIRCACHE.gz";
    std::string fCacheZipFileName = m_downloadMetePath + "/metafile_FILECACHE.gz";
    std::string metaZipFilename  = m_downloadMetePath + "/metafile_META.gz";
    std::string xmetaZipFileName = m_downloadMetePath + "/metafile_XMETA.gz";

    if (!PluginUtils::IsFileExist(dirCacheZipFileName) ||
        !PluginUtils::IsFileExist(fCacheZipFileName) ||
        !PluginUtils::IsFileExist(metaZipFilename) ||
        !PluginUtils::IsFileExist(xmetaZipFileName)) {
        ERRLOG("new meta gzip files not found to uncompress: %s %s %s %s",
            dirCacheZipFileName.c_str(), fCacheZipFileName.c_str(), metaZipFilename.c_str(), xmetaZipFileName.c_str());
        return false;
    }

    // build uncompress command
    std::string moveToDirCmd = "cd " + m_scanInputMetePath + ";";
    std::string zipDirCacheFilesCmd = "tar -I pigz -xf " + dirCacheZipFileName + " -C . &&";
    std::string zipFCacheFilesCmd = "tar -I pigz -xf " + fCacheZipFileName + " -C . &&";
    std::string zipMetaFilesCmd = "tar -I pigz -xf " + metaZipFilename + " -C . &&";
    std::string zipXMetaFilesCmd = "tar -I pigz -xf " + xmetaZipFileName + " -C . ";
    std::string execCmd = moveToDirCmd + zipDirCacheFilesCmd + zipFCacheFilesCmd + zipMetaFilesCmd + zipXMetaFilesCmd;

    bool ret = false;
    int retryCnt = 0;
    while (!ret) {
        INFOLOG("Exec cmd for %d times.", retryCnt);
        if (Module::runShellCmdWithOutput(INFO, MODULE, 0, execCmd, {}, output, errOutput) == Module::SUCCESS) {
            ret = true;
            break;
        }
        ERRLOG("uncompress new gzip meta failed!, cmd: %s", execCmd.c_str());
        for (const std::string& errMessage: errOutput) {
            ERRLOG("%s", errMessage.c_str());
        }
        PluginUtils::RemoveDirectory(m_scanInputMetePath);
        PluginUtils::CreateDirectory(m_scanInputMetePath);
        if (retryCnt++ >= NUMBER3) {
            ERRLOG("retry for three times!");
            break;
        }
    }
    INFOLOG("uncompress new meta gzip files finished");
    return ret;
}

bool NasArchiveRestore::UncompressMetafile() const
{
    INFOLOG("ScanInputMetePath : %s", m_scanInputMetePath.c_str());
    if (!PluginUtils::CreateDirectory(m_scanInputMetePath)) {
        ERRLOG("Create directory failed: %s", m_scanInputMetePath.c_str());
        return false;
    }

    // check if old metafile.zip exists
    std::string oldMetafileZip = m_downloadMetePath + "/" + METAFILE_ZIP_NAME;
    if (!PluginUtils::IsFileExist(oldMetafileZip.c_str())) {
        INFOLOG("old metafile.zip not exists: %s, try to find new meta gzip files", oldMetafileZip.c_str());
        return UnzipNewMetaGZipFiles();
    }

    INFOLOG("old metafile.zip exists: %s, use old metafile.zip", oldMetafileZip.c_str());
    return UnzipOldMetafileZip(oldMetafileZip);
}

bool NasArchiveRestore::StartScanner()
{
    INFOLOG("Enter StartScanner");
    m_lastScannerReportTime = PluginUtils::GetCurrentTimeInSeconds();
    ScanConfig config;
    FillScanConfig(config);
    AddFilterRule(config);

    m_scanner = ScanMgr::CreateScanInst(config);
    if (m_scanner == nullptr) {
        ERRLOG("Start Scanner failed!");
        return false;
    }

    if (m_scanner->Start() != SCANNER_STATUS::SUCCESS) {
        ERRLOG("Start scanner instance failed!");
        m_scanner->Destroy();
        return false;
    }
    INFOLOG("Start scanner instance success");
    return true;
}

inline bool NasArchiveRestore::IsDir(const std::string& name) const
{
    return name.back() == '/';
}

void NasArchiveRestore::AddFilterRule(ScanConfig& scanConfig)
{
    DBGLOG("Enter AddFilterRule");
    std::vector<std::string> dirFilterRule;
    std::vector<std::string> fileFilterRule;
    for (const auto& obj : m_jobInfoPtr->restoreSubObjects) {
        std::string path = obj.name;
        DBGLOG("Filter the path : %s", path.c_str());
        if (IsDir(path)) {
            dirFilterRule.push_back(path);
        } else {
            fileFilterRule.push_back(path);
        }
    }
    scanConfig.dCtrlFltr = dirFilterRule;
    scanConfig.fCtrlFltr = fileFilterRule;
}

void NasArchiveRestore::FillScanConfig(ScanConfig& scanConfig)
{
    INFOLOG("Enter FillScanConfig");
    scanConfig.reqID  = PluginUtils::GenerateHash(m_jobId);
    scanConfig.jobId = m_jobId;

    scanConfig.scanIO = IOEngine::DEFAULT;
    scanConfig.lastBackupTime = 0;

    /* config meta path */
    scanConfig.curDcachePath  = m_scanInputMetePath;
    scanConfig.metaPathForCtrlFiles = m_scanControlFilePath;

    scanConfig.maxOpendirReqCount = NUMBER4000;

    /* 记录线程数 */
    scanConfig.maxCommonServiceInstance = 1;

    scanConfig.usrData = (void*)this;
    scanConfig.scanResultCb = [](void* usrData, std::string) {};
    scanConfig.scanHardlinkResultCb = [](void* usrData, std::string) {};

    scanConfig.scanCtrlMaxDataSize = SCAN_CTRL_MAX_DATA_SIZE;
    scanConfig.scanCtrlMinDataSize = SCAN_CTRL_MIN_DATA_SIZE;
    scanConfig.scanCtrlFileTimeSec = NUMBER5;
    scanConfig.scanCtrlMaxEntriesFullBkup = NUMBER10000;
    scanConfig.scanCtrlMaxEntriesIncBkup = NUMBER1000;
    scanConfig.scanCtrlMinEntriesFullBkup = NUMBER10000;
    scanConfig.scanCtrlMinEntriesIncBkup = NUMBER1000;
    scanConfig.scanMetaFileSize = SCAN_META_FILE_SIZE; // one GB

    scanConfig.maxWriteQueueSize = NUMBER10000;
    scanConfig.scanType = ScanJobType::CONTROL_GEN; // use dcache/fcache to create control file, skip enqueue
    scanConfig.triggerTime = PluginUtils::GetCurrentTimeInSeconds();
    scanConfig.generatorIsFull =  true;
    scanConfig.scanCheckPointEnable = false;
    INFOLOG("EXIT FillScanConfig");
}

void NasArchiveRestore::MonitorScan()
{
    HCP_Log(INFO, MODULE) << "Enter Monitor Scanner" << HCPENDLOG;
    SubJobStatus::type jobStatus = SubJobStatus::RUNNING;
    std::string jobLogLabel = "";

    do {
        m_scanStatus = m_scanner->GetStatus();
        if (m_scanStatus == SCANNER_STATUS::INIT) {
            std::this_thread::sleep_for(std::chrono::seconds(SUBTASK_WAIT_FOR_SCANNER_READY_IN_SEC));
            SendJobReportForAliveness();
            continue;
        }

        UpdateScannerStatistics(m_scanStats, m_jobId);
        ReportScannerRunningStatus();

        SetArchiveJobState();
        if (m_jobState != ArchiveJobState::RUNNING) {
            HCP_Log(INFO, MODULE) << "m_jobState: " << static_cast<int>(m_jobState) << HCPENDLOG;
            break;
        }

        if (IsAbort()) {
            INFOLOG("Scanner - Abort is invocked for TaskID: %s, subTaskID: %s", m_jobId.c_str(), m_subJobId.c_str());
            if (SCANNER_STATUS::SUCCESS != m_scanner->Abort()) {
                HCP_Log(ERR, MODULE) << "Scanner Abort is failed" << HCPENDLOG;
            }
            jobStatus = SubJobStatus::ABORTED;
            return;
        }

        /* Generate backup sub tasks based on the control file generated during scanning */
        if (!CreateSubTasksFromCtrlFile(jobStatus, SUBJOB_TYPE_DATACOPY_COPY_PHASE)) {
            HCP_Log(ERR, MODULE) << "Create subtask failed, abort scan" << HCPENDLOG;
            m_scanner->Abort();
        }
        std::this_thread::sleep_for(std::chrono::seconds(GENERATE_SUBTASK_MONITOR_DUR_IN_SEC));
    } while (true);

    PrintScannerStatistics(m_scanStats);

    if (!CreateSubTasksFromCtrlFile(jobStatus, SUBJOB_TYPE_DATACOPY_COPY_PHASE, true) ||
        !CreateSubTasksFromCtrlFile(jobStatus, SUBJOB_TYPE_DATACOPY_DELETE_PHASE, true) ||
        !CreateSubTasksFromCtrlFile(jobStatus, SUBJOB_TYPE_DATACOPY_HARDLINK_PHASE, true) ||
        !CreateSubTasksFromCtrlFile(jobStatus, SUBJOB_TYPE_DATACOPY_DIRMTIME_PHASE, true)) {
        HCP_Log(ERR, MODULE) << "Create subtask failed" << HCPENDLOG;
        jobLogLabel = "file_plugin_host_backup_scan_fail_label";
        jobStatus = SubJobStatus::FAILED;
        return;
    }

    HCP_Log(INFO, MODULE) << "Exit Monitor Scanner" << HCPENDLOG;
}

bool NasArchiveRestore::ReportScannerRunningStatus()
{
    if ((PluginUtils::GetCurrentTimeInSeconds() - m_lastScannerReportTime) > SCANNER_REPORT_CIRCLE_TIME) {
        ReportJobDetailsWithLabelAndErrcode(JobLogLevel::TASK_LOG_INFO, SubJobStatus::RUNNING, PROGRESS0,
                                            "nas_plugin_hetro_backup_scan_inprogress_label",
                                            INITIAL_ERROR_CODE,
                                            std::to_string(m_scanStats.m_totDirsToBackup),
                                            std::to_string(m_scanStats.m_totFilesToBackup),
                                            PluginUtils::FormatCapacity(m_scanStats.m_totalSizeToBackup),
                                            std::to_string(m_scanStats.m_totDirsToBackup),
                                            std::to_string(m_scanStats.m_totFilesToBackup),
                                            PluginUtils::FormatCapacity(m_scanStats.m_totalSizeToBackup));
        m_lastScannerReportTime = PluginUtils::GetCurrentTimeInSeconds();
    } else {
        ReportJobDetails(JobLogLevel::TASK_LOG_INFO, SubJobStatus::RUNNING, PROGRESS0);
    }
    return true;
}

void NasArchiveRestore::SetArchiveJobState()
{
    if (m_scanStatus == SCANNER_STATUS::COMPLETED) {
        m_jobState = ArchiveJobState::SUCCESS;
    }
    if (m_scanStatus == SCANNER_STATUS::FAILED ||
        m_scanStatus == SCANNER_STATUS::SECONDARY_SERVER_NOT_REACHABLE ||
        m_scanStatus == SCANNER_STATUS::PROTECTED_SERVER_NOT_REACHABLE) {
        m_jobState = ArchiveJobState::FAILED;
    }
    if (m_scanStatus == SCANNER_STATUS::ABORT_IN_PROGRESS || m_scanStatus == SCANNER_STATUS::ABORTED) {
        m_jobState = ArchiveJobState::ABORT;
    }
}

bool NasArchiveRestore::CreateSubTasksFromCtrlFile(SubJobStatus::type &jobStatus, uint32_t subTaskType, bool isFinal)
{
    std::vector<std::string> srcFileList {};
    std::vector<SubJob> subJobList {};
    std::vector<std::string> ctrlFileList {};
    uint32_t validCtrlFileCntr = 0;
    static int64_t lastCreateJobErrTime = 0;

    if (!isFinal && lastCreateJobErrTime != 0 &&
        ((PluginUtils::GetCurrentTimeInSeconds() - lastCreateJobErrTime) < NUMBER180)) {
        HCP_Log(WARN, MODULE) << "lastCreateJobErrTime 180 sec check failed, try later" << HCPENDLOG;
        return true;
    }
    lastCreateJobErrTime = 0;

    if (!CheckFilePathAndGetSrcFileList(m_scanControlFilePath, m_backupControlFilePath, srcFileList)) {
        return false;
    }

    INFOLOG("Enter CreateSubTasksFromCtrlFile, size: %d, isFinal: %d", srcFileList.size(), isFinal);
    for (uint32_t i = 0; i < srcFileList.size(); i++) {
        SendJobReportForAliveness();
        if (IsAbort()) {
            INFOLOG("Abort is invocked for taskid: %s, subtaskid: %s", m_jobId.c_str(), m_subJobId.c_str());
            jobStatus = SubJobStatus::ABORTED;
            return true;
        }

        std::string ctrlFileFullPath = srcFileList[i];
        if (!IsValidCtrlFile(subTaskType, ctrlFileFullPath)) {
            continue;
        }

        if (!isFinal && validCtrlFileCntr++ >= CTRL_FILE_CNT) {
            break;
        }

        std::string dstCtrlFileFullPath = m_backupControlFilePath + "/" + PluginUtils::GetFileName(ctrlFileFullPath);
        std::string dstCtrlFileInCacheFsPath = dstCtrlFileFullPath.substr(m_cacheFsPath.length(), std::string::npos);

        if (!PluginUtils::CopyFile(ctrlFileFullPath, dstCtrlFileFullPath)) {
            return false;
        }
        if (!GenerateSubJobList(subJobList, ctrlFileList, dstCtrlFileInCacheFsPath, ctrlFileFullPath)) {
            return false;
        }
        // We create 10 Jobs at a time. If 10 is not accumulated, continue
        if (subJobList.size() % 10 != 0) {
            continue;
        }
        if (!CheckAndRetryCreateSubTask(subJobList, ctrlFileList, lastCreateJobErrTime, isFinal)) {
            return false;
        }
    }
    if (!CheckAndRetryCreateSubTask(subJobList, ctrlFileList, lastCreateJobErrTime, isFinal)) {
        return false;
    }
    return true;
}

bool NasArchiveRestore::GenerateSubJobList(std::vector<SubJob> &subJobList, std::vector<std::string> &ctrlFileList,
    const std::string &dstCtrlFileRelPath, const std::string &ctrlFileFullPath)
{
    INFOLOG("GenerateSubJobList m_jobId: %s", m_jobId.c_str());
    SubJob subJob {};
    uint32_t subTaskType;

    if (!InitSubTask(subJob, m_jobId, dstCtrlFileRelPath, subTaskType, m_archiveFsId, m_parentDir)) {
        HCP_Log(ERR, MODULE) << "Init subtask failed" << HCPENDLOG;
        return false;
    }
    subJobList.push_back(subJob);
    ctrlFileList.push_back(ctrlFileFullPath);

    return true;
}

int NasArchiveRestore::ExecuteSubJobInner()
{
    INFOLOG("Enter ExecuteSubJobInner");
    HetroBackupSubJob backupSubJob {};
    if (!Module::JsonHelper::JsonStringToStruct(m_subJobInfo->jobInfo, backupSubJob)) {
        ERRLOG("Get restore subjob info failed!");
        return Module::FAILED;
    }
    uint32_t subTaskType = backupSubJob.m_SubTaskType;
    if (subTaskType == SUBJOB_TYPE_CHECK_SUBJOB_PHASE) {
        return ExecuteCheckSubJobInner();
    }
    
    std::shared_ptr<void> defer(nullptr, [&](...) {
        OperateIpsRule(m_IpRuleList, "DELETE");
        OperateIpsRuleForRemoteIp(m_remoteIpRuleList, "DELETE", m_nasShare.ip);
    });
    if (!InitInfo() || !OperateIpsRule(m_IpRuleList, "ADD") ||
        !OperateIpsRuleForRemoteIp(m_remoteIpRuleList, "ADD", m_nasShare.ip)) {
        return Module::FAILED;
    }
    PrintInfo();
    // printsubjobinfo
    if (!CheckConnect()) {
        ERRLOG("CheckConnect Failed!");
        return Module::FAILED;
    }
    if (!InitArchiveClient()) {
        return Module::FAILED;
    }

    if (!InitSubBackupJobResources()) {
        return Module::FAILED;
    }

    if (!StartRestore()) {
        ERRLOG("Start restore failed.");
        return Module::FAILED;
    }

    SubJobStatus::type monitorRet = MonitorRestore();
    // 磁带细粒度恢复失败，错误码为17，提示用户“读取磁带数据失败，请关闭直接恢复开关后重试。”
    ReportTapeNotSupportDirectRestore();
    if (m_backup != nullptr) {
        m_backup->Destroy();
        m_backup.reset();
    }

    INFOLOG("Exit ExecuteSubJobInner");
    return monitorRet == SubJobStatus::COMPLETED ? Module::SUCCESS : Module::FAILED;
}

int NasArchiveRestore::ExecuteCheckSubJobInner()
{
    INFOLOG("Enter Check Subjob Statistics");
    if (!InitInfo()) {
        return Module::SUCCESS;
    }
        // 获取统计总量
    ShareResourceManager::GetInstance().SetResourcePath(m_statsPath, m_jobId);
    CalcuMainBackupStats(m_backupStats);
    if (m_backupStats.m_noOfDirFailed != 0 || m_backupStats.m_noOfFilesFailed != 0) {
        return Module::FAILED;
    }
    return Module::SUCCESS;
}

void NasArchiveRestore::ReportTapeNotSupportDirectRestore()
{
    std::unordered_set<FailedRecordItem, FailedRecordItemHash> failedRecordItem = m_backup->GetFailedDetails();
    for (const auto &item : failedRecordItem) {
        if (item.errNum == TAP_REMIND_ERROR) {
            ReportJobDetailsWithLabelAndErrcode(
                JobLogLevel::TASK_LOG_ERROR, SubJobStatus::RUNNING, PROGRESS0,
                "plugin_tape_not_support_direct_restore_label", 0);
            break;
        }
    }
}

void NasArchiveRestore::IdentifyFileReplaceStrategy(const std::string& strategy)
{
    if (strategy == "0") {
        m_coveragePolicy = RestoreReplacePolicy::OVERWRITE;
    } else if (strategy == "1") {
        m_coveragePolicy = RestoreReplacePolicy::IGNORE_EXIST;
    } else if (strategy == "2") {
        m_coveragePolicy = RestoreReplacePolicy::OVERWRITE_OLDER;
    } else {
        m_coveragePolicy = RestoreReplacePolicy::NONE;
    }
    INFOLOG("Coverage policy: %d", static_cast<int>(m_coveragePolicy));
}

bool NasArchiveRestore::StartRestore()
{
    INFOLOG("Subjob info: %s", m_subJobInfo->jobInfo.c_str());
    // get subjob info include control file, backup phase, fs id
    HetroBackupSubJob subJobInfo;
    if (!Module::JsonHelper::JsonStringToStruct(m_subJobInfo->jobInfo, subJobInfo)) {
        HCP_Log(ERR, MODULE) << "Get restore subjob info failed" << HCPENDLOG;
        return false;
    }

    if (!IsSkipHardlinkPhase(subJobInfo.m_SubTaskType)) {
        return false;
    }

    subJobInfo.m_ControlFile = m_cacheFsPath + subJobInfo.m_ControlFile; // add cache path make full path
    m_archiveClient->SetFsId(subJobInfo.m_Ext); // set arcive s3 id

    BackupParams backupParams = FillRestoreConfig(subJobInfo);
    
    m_backup = FS_Backup::BackupMgr::CreateBackupInst(backupParams);
    if (m_backup == nullptr) {
        HCP_Log(ERR, MODULE) << "Create backup instance failed" << HCPENDLOG;
        return false;
    }

    if (m_backup->Enqueue(subJobInfo.m_ControlFile) != BackupRetCode::SUCCESS) {
        HCP_Log(ERR, MODULE) << "enqueue backup instance failed" << HCPENDLOG;
        return false;
    }
    if (m_backup->Start() != BackupRetCode::SUCCESS) {
        HCP_Log(ERR, MODULE) << "Start backup task failed" << HCPENDLOG;
        return false;
    }
    return true;
}

bool NasArchiveRestore::IsSkipHardlinkPhase(uint32_t subTaskType)
{
    if (!(subTaskType == SUBJOB_TYPE_DATACOPY_HARDLINK_PHASE ||
        subTaskType == SUBJOB_TYPE_DATACOPY_DELETE_PHASE)) {
        return true;
    }
    std::string hardlinkSupportFile = m_scanInputMetePath + "/support_hardlink";
    INFOLOG("check hardlink support file: %s", hardlinkSupportFile.c_str());
    if (IsFileExist(hardlinkSupportFile)) {
        INFOLOG("hardlink support file exist.");
        return true;
    }
    // delete/hardlink phase
    HetroLastCopyDetails backupCopyInfo {};
    if (!ReadBackupCopyFromFile(m_cacheMdPath + BACKUP_COPY_METAFILE, backupCopyInfo)) {
        HCP_Log(ERR, MODULE) << "ReadBackupCopyFromFile failed " << HCPENDLOG;
        m_jobState = ArchiveJobState::SKIP_PHASE;
        return false;
    }
    INFOLOG("isArchiveSupportHardlink is %s.", backupCopyInfo.m_isArchiveSupportHardlink.c_str());
    if (backupCopyInfo.m_isArchiveSupportHardlink == "true") {
        return true;
    }
    WARNLOG("Skip delete or hardlink phase");
    m_jobState = ArchiveJobState::SKIP_PHASE;
    return false;
}

BackupParams NasArchiveRestore::FillRestoreConfig(const HetroBackupSubJob& subJobInfo)
{
    HCP_Log(INFO, MODULE) << "Enter FillRestoreConfig" << HCPENDLOG;

    BackupParams backupParams {};
    backupParams.backupType = BackupType::RESTORE;
    backupParams.scanAdvParams.metaFilePath = m_scanInputMetePath;
    HCP_Log(INFO, MODULE) << "backupParams.scanAdvParams.metaFilePath is: " << m_scanInputMetePath <<  HCPENDLOG;

    if (subJobInfo.m_SubTaskType == SUBJOB_TYPE_DATACOPY_COPY_PHASE)
        backupParams.phase = BackupPhase::COPY_STAGE;
    else if (subJobInfo.m_SubTaskType == SUBJOB_TYPE_DATACOPY_HARDLINK_PHASE)
        backupParams.phase = BackupPhase::HARDLINK_STAGE;
    else if (subJobInfo.m_SubTaskType == SUBJOB_TYPE_DATACOPY_DELETE_PHASE)
        backupParams.phase = BackupPhase::DELETE_STAGE;
    else if (subJobInfo.m_SubTaskType == SUBJOB_TYPE_DATACOPY_DIRMTIME_PHASE)
        backupParams.phase = BackupPhase::DIR_STAGE;

    CommonParams commonParams {};
    commonParams.maxBufferCnt = NUMBER10;
    commonParams.maxBufferSize = BACKUP_MAX_BUF_SIZE; // 10kb
    commonParams.maxErrorFiles = NUMBER100;
    commonParams.backupDataFormat = m_isAggCopy ? BackupDataFormat::AGGREGATE : BackupDataFormat::NATIVE;
    commonParams.restoreReplacePolicy = m_coveragePolicy;
    commonParams.jobId = m_jobId;
    commonParams.subJobId = m_subJobId;
    commonParams.reqID = PluginUtils::GenerateHash(m_subJobId);
    commonParams.writeAcl = true;
    // nfs 写死, cifs 去获取
    commonParams.blockSize = FOUR_MB;
    
    // 是磁带恢复的话，每4M有18个字节的控制头，需要去掉
    if (m_isTapeRestore) {
        commonParams.blockSize = TAPE_READ_BUFFER_SIZE;
    }
    backupParams.commonParams = commonParams;

    backupParams.commonParams.metaPath = m_cacheMdPath;
    if (m_isAggCopy) {
        backupParams.commonParams.maxAggregateFileSize = std::stoul(m_aggCopyInfo.maxSizeAfterAggregate);
        backupParams.commonParams.maxFileSizeToAggregate = std::stoul(m_aggCopyInfo.maxSizeToAggregate);
    }

    FillSrcParams(backupParams, subJobInfo);
    FillDstParams(backupParams);

    return backupParams;
}

void NasArchiveRestore::FillSrcParams(BackupParams& backupParams, const HetroBackupSubJob& subJobInfo)
{
    backupParams.srcEngine = BackupIOEngine::ARCHIVE_CLIENT;
    backupParams.srcAdvParams = std::make_shared<ArchiveRestoreAdvanceParams>(m_archiveClient);

    std::dynamic_pointer_cast<ArchiveRestoreAdvanceParams>(backupParams.srcAdvParams)->dataPath =
        subJobInfo.m_parentDir.empty() ? m_dataPath : subJobInfo.m_parentDir;
    HCP_Log(INFO, MODULE) << "resourcePath is: " <<
        std::dynamic_pointer_cast<ArchiveRestoreAdvanceParams>(backupParams.srcAdvParams)->dataPath <<  HCPENDLOG;
}

void NasArchiveRestore::FillDstParams(BackupParams& backupParams)
{
    if (IsRestoreByNFS()) {
        FillParamsNFS(backupParams);
    } else if (IsRestoreByCIFS()) {
        FillParamsCIFS(backupParams);
    } else {
        ERRLOG("No available protocol");
    }
}

void NasArchiveRestore::FillParamsNFS(BackupParams& backupParams)
{
    backupParams.dstEngine = BackupIOEngine::LIBNFS;

    std::shared_ptr<LibnfsBackupAdvanceParams> dstAdvParams = std::make_shared<LibnfsBackupAdvanceParams>();
    dstAdvParams->jobStartTime = PluginUtils::GetCurrentTimeInSeconds();
    dstAdvParams->protocolVersion = m_nasShare.protocolVersion;
    dstAdvParams->ip = m_nasShare.ip;
    dstAdvParams->sharePath = m_nasShare.sharePath;
    dstAdvParams->dataPath = m_fineRestorePath;
    backupParams.dstAdvParams = dstAdvParams;
}

void NasArchiveRestore::FillParamsCIFS(BackupParams& backupParams)
{
    backupParams.dstEngine = BackupIOEngine::LIBSMB;

    std::shared_ptr<LibsmbBackupAdvanceParams> dstAdvParams = std::make_shared<LibsmbBackupAdvanceParams>();
    dstAdvParams->server = m_nasShare.ip;
    dstAdvParams->share = m_nasShare.sharePath;
    dstAdvParams->version = m_nasShare.protocolVersion;
    dstAdvParams->encryption = false;
    dstAdvParams->sign = false;
    dstAdvParams->timeout = ONE_MINUTE;
    std::string agentHomePath = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    dstAdvParams->krb5ConfigFile = agentHomePath + KRB5CONFIGPREFIX + m_jobId + KRB5CONFIGPOSTFIX;
    dstAdvParams->krb5CcacheFile = agentHomePath + KRB5CCNAMEPREFIX + m_jobId;
    dstAdvParams->user = m_nasShare.auth.authkey;
    dstAdvParams->password = m_nasShare.auth.authPwd;
    dstAdvParams->domain = m_nasShare.domainName;
    dstAdvParams->rootPath = CutPrefixSlash(m_fineRestorePath);

    if (m_nasShare.auth.authType == AuthType::type::KERBEROS) {
        dstAdvParams->authType = "krb5";
    } else {
        dstAdvParams->authType = "ntlmssp";
    }
    backupParams.dstAdvParams = dstAdvParams;
}

// 去除文件名前缀 / . 适配根目录文件的 //a.out 的情况
std::string NasArchiveRestore::CutPrefixSlash(const std::string& path) const
{
    int offset = 0;
    std::string p;
    for (std::size_t i = 0; i < path.size(); i++) {
        p = path[offset];
        if (p == "/" || p == ".") {
            offset++;
            continue;
        }
        break;
    }
    return path.substr(offset, path.size());
}

void NasArchiveRestore::ReportCopyPhaseStart()
{
    LockBackupStatsResource(m_jobId);
    GetBackupStatsResource(m_jobId, m_backupStats);
    // 从数据库中读取mainStats，若上次上报时间为零，表明未上报开始恢复数据
    if (m_backupStats.m_lastLogReportTime != 0) {
        DBGLOG("Not first sub backup job, don't need report!");
        UnlockBackupStatsResource(m_jobId);
        return;
    }
    m_backupStats.m_lastLogReportTime = PluginUtils::GetCurrentTimeInSeconds();
    UpdateBackupStatsResource(m_jobId, m_backupStats);
    UnlockBackupStatsResource(m_jobId);
    INFOLOG("Report copy phase start to pm");
    ReportJobDetailsWithLabelAndErrcode(JobLogLevel::TASK_LOG_INFO, SubJobStatus::RUNNING, PROGRESS0,
        "nas_plugin_archive_restore_data_start_label", 0);
}

SubJobStatus::type NasArchiveRestore::MonitorRestore()
{
    HCP_Log(INFO, MODULE) << "Enter MonitorRestore" << HCPENDLOG;
    SubJobStatus::type jobStatus = SubJobStatus::RUNNING;
    ReportCopyPhaseStart();
    do {
        m_backupStatus = m_backup->GetStatus();
        HCP_Log(INFO, MODULE) << "m_backupStatus:" << static_cast<int>(m_backupStatus) << HCPENDLOG;
        UpdateSubBackupStats();
        UpdateMainStatsIfReport();
        if (!IsBackupStatusInprogress(jobStatus)) {
            HCP_Log(DEBUG, MODULE) << "m_backupStatus: " << static_cast<int>(m_backupStatus) << HCPENDLOG;
            break;
        }

        if (IsAbort()) {
            HCP_Log(INFO, MODULE) << "Backup - Abort is invocked for taskid: " << m_jobId
                << ", subtaskid: " << m_subJobId << HCPENDLOG;
            if (BackupRetCode::SUCCESS != m_backup->Abort()) {
                HCP_Log(ERR, MODULE) << "backup Abort is failed" << HCPENDLOG;
            }
            jobStatus = SubJobStatus::ABORTED;
            return jobStatus;
        }
        std::this_thread::sleep_for(std::chrono::seconds(EXECUTE_SUBTASK_MONITOR_DUR_IN_SEC));
    } while (true);
    UpdateSubBackupStats();
    HCP_Log(INFO, MODULE) << "Exit Monitor Backup" << HCPENDLOG;
    return jobStatus;
}

bool NasArchiveRestore::UpdateSubBackupStats()
{
    BackupStats currStats = m_backup->GetStats();
    m_dataSize = currStats.noOfBytesCopied / NUMBER1024;
    BackupStatisticClassConvertToStruct(currStats, m_subBackupStats,
        static_cast<uint32_t>(m_backup->m_backupParams.phase));
    ShareResourceManager::GetInstance().Wait(ShareResourceType::BACKUP, m_subJobId);
    bool ret = ShareResourceManager::GetInstance().UpdateResource(
        ShareResourceType::BACKUP, m_subJobId, m_subBackupStats);
    ShareResourceManager::GetInstance().Signal(ShareResourceType::BACKUP, m_subJobId);
    if (!ret) {
        ERRLOG("Update sub job stats failed, jobId: %s, sub jobId: %s", m_jobId.c_str(), m_subJobId.c_str());
        return false;
    }
    return true;
}

void NasArchiveRestore::UpdateMainStatsIfReport()
{
    LockBackupStatsResource(m_jobId);
    GetBackupStatsResource(m_jobId, m_backupStats);
    time_t lastUpdateTime = m_backupStats.m_lastLogReportTime;
    time_t currentTime = PluginUtils::GetCurrentTimeInSeconds();
    if ((currentTime - lastUpdateTime) < BACKUP_REPORT_CIRCLE_TIME_IN_SEC) {
        UnlockBackupStatsResource(m_jobId);
        INFOLOG("The report time is not reached. lastTime: %d, currTime: %d, Report Interval: %d",
            lastUpdateTime, currentTime, currentTime - lastUpdateTime);
        return;
    }
    HetroNativeBackupStats mainBackupStats {};
    mainBackupStats.m_lastLogReportTime = lastUpdateTime;
    if (!CalcuMainBackupStats(mainBackupStats)) {
        UnlockBackupStatsResource(m_jobId);
        return;
    }
    if (ReportBackupRunningStatus(mainBackupStats)) {
        mainBackupStats.m_lastLogReportTime = currentTime;
    }
    bool ret = UpdateBackupStatsResource(m_jobId, mainBackupStats);
    UnlockBackupStatsResource(m_jobId);
    if (!ret) {
        ERRLOG("Update main job backup stats failed, jobId: %s, sub jobId: %s", m_jobId.c_str(), m_subJobId.c_str());
    }
}

bool NasArchiveRestore::CalcuMainBackupStats(HetroNativeBackupStats& mainBackupStats)
{
    INFOLOG("Enter CalcuMainBackupStats: %s", m_statsPath.c_str());
    uint64_t noOfDirCopiedFromDirMtimePhase = 0;
    std::vector<std::string> statsList;
    if (!PluginUtils::GetFileListInDirectory(m_statsPath, statsList)) {
        ERRLOG("Get backup stats list failed, jobId: %s", m_jobId.c_str());
        return false;
    }
    HetroNativeBackupStats subStats;
    for (const std::string& path : statsList) {
        std::string::size_type pos = path.find(BACKUP_KEY_SUFFIX);
        if (pos == std::string::npos) {
            continue;
        }
        std::string subJobId = path.substr(m_statsPath.length() + NUMBER1, m_subJobId.length());
        DBGLOG("CalcuMainBackupStats, path: %s, jobId: %s, subJobId: %s",
            path.c_str(), m_jobId.c_str(), subJobId.c_str());
        bool ret = ShareResourceManager::GetInstance().QueryResource(path, subStats);
        if (!ret) {
            ERRLOG("Query failed, jobId: %s, subJobId: %s, path: %s", m_jobId.c_str(), subJobId.c_str(), path.c_str());
            return false;
        }
        INFOLOG("CalcuMainBackupStats, sub: copied %llu, total bytes %llu; main: copied %llu, total bytes %llu,"
            "subJobPhrase %d", subStats.m_noOfBytesCopied, subStats.m_noOfBytesToBackup,
            mainBackupStats.m_noOfBytesCopied, mainBackupStats.m_noOfBytesToBackup, subStats.m_subJobPhrase);
        if (subStats.m_subJobPhrase == SUBJOB_TYPE_DATACOPY_DIRMTIME_PHASE) {
            noOfDirCopiedFromDirMtimePhase += subStats.m_noOfDirCopied;
        } else {
            mainBackupStats += subStats;
        }
    }
    mainBackupStats.m_noOfDirCopied = noOfDirCopiedFromDirMtimePhase;

    PrintBackupStatistics(mainBackupStats);
    return true;
}

void NasArchiveRestore::BackupStatisticClassConvertToStruct(
    const BackupStats& backupStats, HetroNativeBackupStats& backupStatistic, uint32_t backupPhrase)
{
    HCP_Log(DEBUG, MODULE) << "start to BackupStats class convert to HetroNativeBackupStats struct" << HCPENDLOG;
    backupStatistic.m_subJobPhrase       = backupPhrase;
    backupStatistic.m_noOfDirToBackup    = backupStats.noOfDirToBackup;
    backupStatistic.m_noOfFilesToBackup  = backupStats.noOfFilesToBackup;
    backupStatistic.m_noOfBytesToBackup  = backupStats.noOfBytesToBackup;
    backupStatistic.m_noOfDirToDelete    = backupStats.noOfDirToDelete;
    backupStatistic.m_noOfFilesToDelete  = backupStats.noOfFilesToDelete;
    backupStatistic.m_noOfDirCopied      = backupStats.noOfDirCopied;
    backupStatistic.m_noOfFilesCopied    = backupStats.noOfFilesCopied;
    backupStatistic.m_noOfBytesCopied    = backupStats.noOfBytesCopied;
    backupStatistic.m_noOfDirDeleted     = backupStats.noOfDirDeleted;
    backupStatistic.m_noOfFilesDeleted   = backupStats.noOfFilesDeleted;
    backupStatistic.m_noOfDirFailed      = backupStats.noOfDirFailed;
    backupStatistic.m_noOfFilesFailed    = backupStats.noOfFilesFailed;
    backupStatistic.m_noOfSrcRetryCount  = backupStats.noOfSrcRetryCount;
    backupStatistic.m_noOfDstRetryCount  = backupStats.noOfDstRetryCount;
}

bool NasArchiveRestore::ReportBackupRunningStatus(const HetroNativeBackupStats& mainBackupStats)
{
    if (mainBackupStats.m_noOfBytesCopied != 0) {
        DBGLOG("ReportBackupRunningStatus, noOfBytesCopied: %d", mainBackupStats.m_noOfBytesCopied);
        ReportJobDetailsWithLabelAndErrcode(JobLogLevel::TASK_LOG_INFO, SubJobStatus::RUNNING, PROGRESS0,
                                            "nas_plugin_hetro_restore_data_inprogress_label", INITIAL_ERROR_CODE,
                                            std::to_string(mainBackupStats.m_noOfDirCopied),
                                            std::to_string(mainBackupStats.m_noOfFilesCopied),
                                            PluginUtils::FormatCapacity(mainBackupStats.m_noOfBytesCopied));
        return true;
    } else {
        ReportJobDetailsWithLabelAndErrcode(JobLogLevel::TASK_LOG_INFO, SubJobStatus::RUNNING, PROGRESS0,
            "", 0);
        return false;
    }
}

bool NasArchiveRestore::IsBackupStatusInprogress(SubJobStatus::type &jobStatus)
{
    if (m_backupStatus == BackupPhaseStatus::COMPLETED) {
        INFOLOG("Monitor Backup - BackupPhaseStatus::COMPLETED");
        jobStatus = SubJobStatus::COMPLETED;
        return false;
    } else if (m_backupStatus == BackupPhaseStatus::FAILED ||
        m_backupStatus == BackupPhaseStatus::FAILED_NOACCESS ||
        m_backupStatus == BackupPhaseStatus::FAILED_NOSPACE ||
        m_backupStatus == BackupPhaseStatus::FAILED_SEC_SERVER_NOTREACHABLE ||
        m_backupStatus == BackupPhaseStatus::FAILED_PROT_SERVER_NOTREACHABLE) {
        ERRLOG("Monitor Backup - BackupPhaseStatus::FAILED");
        jobStatus = SubJobStatus::FAILED;
        return false;
    } else if (m_backupStatus == BackupPhaseStatus::ABORTED) {
        INFOLOG("Monitor Backup - BackupPhaseStatus::ABORTED");
        jobStatus = SubJobStatus::ABORTED;
        return false;
    } else if (m_backupStatus == BackupPhaseStatus::ABORT_INPROGRESS) {
        INFOLOG("Monitor Backup - BackupPhaseStatus::ABORT_INPROGRESS");
        jobStatus = SubJobStatus::ABORTING;
        return true;
    } else if (m_backupStatus == BackupPhaseStatus::INPROGRESS) {
        INFOLOG("Monitor Backup - BackupPhaseStatus::RUNNING");
        jobStatus = SubJobStatus::RUNNING;
        return true;
    }
    return true;
}

void NasArchiveRestore::PrintBackupStatistics(const HetroNativeBackupStats &backupStatistic)
{
    INFOLOG("Backup jobId:             \t%s", m_jobId.c_str());
    INFOLOG("Backup subjobId:          \t%s", m_subJobId.c_str());
    INFOLOG("Backup status:            \t%d", static_cast<int>(m_backupStatus));
    INFOLOG("Backup noOfDirToBackup:   \t%d", backupStatistic.m_noOfDirToBackup);
    INFOLOG("Backup noOfFilesToBackup: \t%d", backupStatistic.m_noOfFilesToBackup);
    INFOLOG("Backup noOfBytesToBackup: \t%lu", backupStatistic.m_noOfBytesToBackup);
    INFOLOG("Backup noOfDirToDelete:   \t%d", backupStatistic.m_noOfDirToDelete);
    INFOLOG("Backup noOfFilesToDelete: \t%d", backupStatistic.m_noOfFilesToDelete);
    INFOLOG("Backup noOfDirCopied:     \t%d", backupStatistic.m_noOfDirCopied);
    INFOLOG("Backup noOfFilesCopied:   \t%d", backupStatistic.m_noOfFilesCopied);
    INFOLOG("Backup noOfBytesCopied:   \t%lu", backupStatistic.m_noOfBytesCopied);
    INFOLOG("Backup noOfDirDeleted:    \t%d", backupStatistic.m_noOfDirDeleted);
    INFOLOG("Backup noOfFilesDeleted:  \t%d", backupStatistic.m_noOfFilesDeleted);
    INFOLOG("Backup noOfDirFailed:     \t%d", backupStatistic.m_noOfDirFailed);
    INFOLOG("Backup noOfFilesFailed:   \t%d", backupStatistic.m_noOfFilesFailed);
    INFOLOG("Backup noOfSrcRetryCount: \t%d", backupStatistic.m_noOfSrcRetryCount);
    INFOLOG("Backup noOfDstRetryCount: \t%d", backupStatistic.m_noOfDstRetryCount);
}

int NasArchiveRestore::PostJobInner()
{
    if (!InitInfo()) {
        return Module::FAILED;
    }
    PrintInfo();
    if (!OperateIpsRule(m_IpRuleList, "DELETE") ||
        !OperateIpsRuleForRemoteIp(m_remoteIpRuleList, "DELETE", m_nasShare.ip)) {
        ERRLOG("delete ip rule failed");
        return Module::FAILED;
    }
    // 获取统计总量
    ShareResourceManager::GetInstance().SetResourcePath(m_statsPath, m_jobId);

    if (!InitArchiveClient()) {
        return Module::FAILED;
    }

    if (m_archiveClient->EndRecover() != Module::SUCCESS) {
        ERRLOG("Check disconnect archive server failed.");
        return Module::FAILED;
    }

    if (m_archiveClient->DisConnect() != Module::SUCCESS) {
        ERRLOG("Check disconnect archive server failed.");
        // post job no need to return failed
    }

    CleanCache();

    INFOLOG("Exit PostJobInner");
    return Module::SUCCESS;
}

bool NasArchiveRestore::InitMainResources()
{
    INFOLOG("Enter InitMainResources stats path:%s", m_statsPath.c_str());
    ShareResourceManager::GetInstance().SetResourcePath(m_statsPath, m_jobId);
    bool ret = ShareResourceManager::GetInstance().InitResource(ShareResourceType::SCAN, m_jobId, m_scanStats);
    if (!ret) {
        HCP_Log(ERR, MODULE) << "Init scan shared resourace failed" << HCPENDLOG;
        return ret;
    }
    ret = CreateBackupStatsResource(m_jobId, m_backupStats);
    if (!ret) {
        HCP_Log(ERR, MODULE) << "Create main backup shared resourace failed" << HCPENDLOG;
        return ret;
    }
    return true;
}

bool NasArchiveRestore::InitSubBackupJobResources()
{
    INFOLOG("Enter InitSubBackupJobResources");
    ShareResourceManager::GetInstance().SetResourcePath(m_statsPath, m_subJobId);
    bool ret = ShareResourceManager::GetInstance().InitResource(
        ShareResourceType::BACKUP, m_subJobId, m_subBackupStats);
    if (!ret) {
        HCP_Log(ERR, MODULE) << "Init sub backup shared resourace failed" << HCPENDLOG;
        return ret;
    }
    return ret;
}

bool NasArchiveRestore::QueryMainScanResources()
{
    INFOLOG("Enter QueryMainScanResources");
    ShareResourceManager::GetInstance().Wait(ShareResourceType::SCAN, m_jobId);
    bool ret = ShareResourceManager::GetInstance().QueryResource(ShareResourceType::SCAN, m_jobId, m_scanStats);
    ShareResourceManager::GetInstance().Signal(ShareResourceType::SCAN, m_jobId);
    if (!ret) {
        HCP_Log(ERR, MODULE) << "Query scan shared resourace failed" << HCPENDLOG;
    }
    return ret;
}

bool NasArchiveRestore::DeleteSharedResources()
{
    INFOLOG("Enter DeleteSharedResources");
    ShareResourceManager::GetInstance().DeleteResource(ShareResourceType::SCAN, m_jobId);
    DeleteBackupStatsResource(m_jobId);
    // Delete subJob share resource
    PluginUtils::RemoveDirectory(m_statsPath);
    // Delete kerberos file created during InitKerberos
    if (IsRestoreByCIFS() && IsUseKerberos()) {
        CleanKrbFilesForCifs(m_jobId);
    }
    return true;
}

void NasArchiveRestore::UpdateScannerStatistics(HetroNativeScanStatistics &scanStatistics, std::string &jobId)
{
    ScanStatistics stats = m_scanner->GetStatistics();
    scanStatistics.m_scanDuration = stats.mScanDuration;
    scanStatistics.m_totDirs = stats.mTotDirs;
    scanStatistics.m_totFiles = stats.mTotFiles;
    scanStatistics.m_totalSize = stats.mTotalSize;
    scanStatistics.m_totDirsToBackup = stats.mTotDirsToBackup;
    scanStatistics.m_totFilesToBackup = stats.mTotFilesToBackup;
    scanStatistics.m_totFilesDeleted = stats.mTotFilesDeleted;
    scanStatistics.m_totDirsDeleted = stats.mTotDirsDeleted;
    scanStatistics.m_totalSizeToBackup = stats.mTotalSizeToBackup;
    scanStatistics.m_totalControlFiles = stats.mTotalControlFiles;
    scanStatistics.m_totFailedDirs = stats.mTotFailedDirs;
    scanStatistics.m_totFailedFiles = stats.mTotFailedFiles;

    PrintScannerStatistics(scanStatistics);

    ShareResourceManager::GetInstance().Wait(ShareResourceType::SCAN, jobId);
    ShareResourceManager::GetInstance().UpdateResource(ShareResourceType::SCAN, jobId, scanStatistics);
    ShareResourceManager::GetInstance().Signal(ShareResourceType::SCAN, jobId);
    return;
}

void NasArchiveRestore::PrintScannerStatistics(HetroNativeScanStatistics &scanStatistics)
{
    INFOLOG("Scan status:                \t%d", static_cast<int>(m_scanStatus));
    INFOLOG("Num of Dirs Scanned:        \t%d", scanStatistics.m_totDirs);
    INFOLOG("Num of Files Scanned:       \t%d", scanStatistics.m_totFiles);
    INFOLOG("Num of Dirs Failed to Scan: \t%d", scanStatistics.m_totFailedDirs);
    INFOLOG("Num of Files Failed to Scan:\t%d", scanStatistics.m_totFailedFiles);

    INFOLOG("Num of Dirs to Bkup:        \t%d", scanStatistics.m_totDirsToBackup);
    INFOLOG("Num of Files to Bkup:       \t%d", scanStatistics.m_totFilesToBackup);
    INFOLOG("Num of Dirs to Del:         \t%d", scanStatistics.m_totDirsDeleted);
    INFOLOG("Num of Files to Del:        \t%d", scanStatistics.m_totFilesDeleted);
    INFOLOG("Num of Control Files:       \t%d", scanStatistics.m_totalControlFiles);

    INFOLOG("Total Size:                 \t%s",
        PluginUtils::FormatCapacity(scanStatistics.m_totalSize).c_str());
    INFOLOG("Total Size to Bkup:         \t%s",
        PluginUtils::FormatCapacity(scanStatistics.m_totalSizeToBackup).c_str());

    return;
}

bool NasArchiveRestore::CheckFilePathAndGetSrcFileList(std::string srcDir, std::string dstDir,
    std::vector<std::string> &srcFileList)
{
    if (!Module::CFile::DirExist(srcDir.c_str())) {
        HCP_Log(ERR, MODULE) << "SrcDir does not exist: " << srcDir << HCPENDLOG;
        return false;
    }

    if (!Module::CFile::DirExist(dstDir.c_str())) {
        HCP_Log(ERR, MODULE) << "DstDir does not exist: " << dstDir << HCPENDLOG;
        return false;
    }

    if (!PluginUtils::GetFileListInDirectory(srcDir, srcFileList)) {
        HCP_Log(ERR, MODULE) << "Get filelist for dir failed: " << srcDir << HCPENDLOG;
        return false;
    }

    return true;
}

void NasArchiveRestore::CleanCache()
{
    // Hint:: clean download meta here, remove CacheFsPath
    WARNLOG("nas archive restore cacheFs not cleaned!");
}

void NasArchiveRestore::SetArchiveSourceInfo()
{
    /*
     * remotePath.path
     *     E6000: /Fileset_CacheDataRepository_su0/source_policy_07bc7411-4193-4a53-a88d-1d1cb6ae51cd/Context
     *     X8000: /Fileset_CacheDataRepository_su0/source_policy_07bc7411-4193-4a53-a88d-1d1cb6ae51cd_Context
     * repository type
     *     1: data仓
     *     2: cache仓
     * remotePath type
     *     0: meta区
     *     1: data区
     */
    m_copyId = m_jobInfoPtr->copies[0].id; // 副本ID
    m_protectSubtype = m_jobInfoPtr->copies[0].protectObject.subType;
    Json::Value extendInfoValue;
    Module::JsonHelper::JsonStringToJsonValue(m_jobInfoPtr->copies[0].extendInfo, extendInfoValue);
    for (Json::Value::ArrayIndex i = 0; i != extendInfoValue["repositories"].size(); i++) {
        if (extendInfoValue["repositories"][i]["type"] != 1) {
            continue;
        }
        for (Json::Value::ArrayIndex j = 0; j != extendInfoValue["repositories"][i]["remotePath"].size(); j++) {
            if (extendInfoValue["repositories"][i]["remotePath"][j]["type"] == 0) {
                m_sourceContextMd =
                    RemoveShareName(extendInfoValue["repositories"][i]["remotePath"][j]["path"].asString());
            } else if (extendInfoValue["repositories"][i]["remotePath"][j]["type"] == 1) {
                m_sourceContext =
                    RemoveShareName(extendInfoValue["repositories"][i]["remotePath"][j]["path"].asString());
            }
        }
    }
    INFOLOG("Copy id: %s, Meta file path prefix: %s, data file path prefix: %s",
        m_copyId.c_str(), m_sourceContextMd.c_str(), m_sourceContext.c_str());
}

std::string NasArchiveRestore::RemoveShareName(const std::string& path)
{
    // 定位 "source_policy_" 子字符串的位置
    size_t pos = path.find("source_policy_");
    if (pos == std::string::npos) {
        // 如果未找到，返回空字符串或输入路径
        return "";
    }

    // 提取从 "source_policy_" 开始的部分
    std::string transformedPath = path.substr(pos);

    return transformedPath;
}